#pragma once

#include "base/allocation.h"

#include <stdlib.h>

namespace l8
{

// ----------------------------------------------------------------------------
// General helper functions

// Returns true iff x is a power of 2.  Does not work for zero.
template <typename T>
static inline bool IsPowerOf2(T x)
{
    return (x & (x - 1)) == 0;
}


// The C++ standard leaves the semantics of '>>' undefined for
// negative signed operands. Most implementations do the right thing,
// though.
static inline int ArithmeticShiftRight(int x, int s)
{
    return x >> s;
}


// Compute the 0-relative offset of some absolute value x of type T.
// This allows conversion of Addresses and integral types into
// 0-relative int offsets.
template <typename T>
static inline intptr_t OffsetFrom(T x)
{
    return x - static_cast<T>(0);
}


// Compute the absolute value of type T for some 0-relative offset x.
// This allows conversion of 0-relative int offsets into Addresses and
// integral types.
template <typename T>
static inline T AddressFrom(intptr_t x)
{
    return static_cast<T>(0) + x;
}


// Return the largest multiple of m which is <= x.
template <typename T>
static inline T RoundDown(T x, int m)
{
    ASSERT(IsPowerOf2(m));
    return AddressFrom<T>(OffsetFrom(x) & -m);
}

// Return the smallest multiple of m which is >= x.
template <typename T>
static inline T RoundUp(T x, int m)
{
    return RoundDown(x + m - 1, m);
}

// Returns the smallest power of two which is >= x. If you pass in a
// number that is already a power of two, it is returned as is.
uint32_t RoundUpToPowerOf2(uint32_t x);


template <typename T>
static inline bool IsAligned(T value, T alignment)
{
    ASSERT(IsPowerOf2(alignment));
    return (value & (alignment - 1)) == 0;
}


// Returns true if (addr + offset) is aligned.
static inline bool IsAddressAligned(Address addr, int alignment, int offset)
{
    int offs = OffsetFrom(addr + offset);
    return IsAligned(offs, alignment);
}


// Returns the maximum of the two parameters.
template <typename T>
static inline T Max(T a, T b)
{
    return a < b ? b : a;
}


// Returns the minimum of the two parameters.
template <typename T>
static inline T Min(T a, T b)
{
    return a < b ? a : b;
}

} // namespace l8
